package com.wteam.dragon.system.apps.security.controller;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import com.wf.captcha.ArithmeticCaptcha;
import com.wteam.dragon.model.service.UserService;
import com.wteam.dragon.shop.config.WxMaConfiguration;
import com.wteam.dragon.shop.config.WxMaProperties;
import com.wteam.dragon.system.aop.log.log.Log;
import com.wteam.dragon.system.apps.security.annotation.AnonymousAccess;
import com.wteam.dragon.system.apps.security.config.RsaProperties;
import com.wteam.dragon.system.apps.security.config.SecurityProperties;
import com.wteam.dragon.system.apps.security.config.token.OpenidAuthenticationToken;
import com.wteam.dragon.system.apps.security.service.DetailsServiceImpl;
import com.wteam.dragon.system.apps.security.service.OnlineUserService;
import com.wteam.dragon.system.apps.security.service.deo.JwtUserDto;
import com.wteam.dragon.system.apps.security.token.TokenProvider;
import com.wteam.dragon.system.apps.security.utils.RsaUtils;
import com.wteam.dragon.system.apps.security.vo.AuthUser;
import com.wteam.dragon.system.basebean.bean.ResultMessage;
import com.wteam.dragon.system.exception.BadRequestException;
import com.wteam.dragon.system.util.RedisUtils;
import com.wteam.dragon.system.util.StringUtils;
import com.wteam.dragon.system.util.UserUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 授权、根据token获取用户详细信息
 * @author 陈晓辉
 * @date 2020-03-16
 */
@Slf4j
@RestController
@RequestMapping("admin/login")
@Api(tags = "系统：系统授权接口")
@RequiredArgsConstructor
public class AuthController {
    @Value("${loginCode.expiration}")
    private Long expiration;
    @Value("${rsa.private_key}")
    private String privateKey;
    @Value("${single.login:false}")
    private Boolean singleLogin;

    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final DetailsServiceImpl detailsService;
    private final OnlineUserService onlineUserService;
    private final TokenProvider tokenProvider;
    private final WxMaProperties wxMaProperties;
    private final UserService userService;
    private final AuthenticationManager authenticationManager;

    @Log("小程序登录")
    @ApiOperation("小程序登录")
    @AnonymousAccess
    @PostMapping(value = "/miniAppLogin")
    public ResultMessage miniAppLogin(String code, String iv, String rawData, String signature, String encryptedData, HttpServletRequest request) throws Exception {
        if(StringUtils.isEmpty(code)){
            throw new BadRequestException("参数错误");
        }
        //获得微信用户的openid
        WxMaProperties.Config config = wxMaProperties.getConfigs().get(0);
        String appid = config.getAppid();
        final WxMaService wxService = WxMaConfiguration.getMaService(appid);
        WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(code);
        // 用户信息校验
        if (!wxService.getUserService().checkUserInfo(session.getSessionKey(), rawData, signature)) {
            throw new BadRequestException("用户信息校验失败");
        }
        // 解密用户信息
        WxMaUserInfo userInfo = wxService.getUserService().getUserInfo(session.getSessionKey(), encryptedData, iv);
        //处理用户 根据用户的openId查找用户，如果没有该用户则创建一个新的用户
        userService.createUser(userInfo);
        //进行用户验证
        OpenidAuthenticationToken openidAuthenticationToken = new OpenidAuthenticationToken(userInfo.getOpenId());
        Authentication authentication = authenticationManager.authenticate(openidAuthenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token, request);
        // 返回 token 与 用户信息
        Map<String,Object> authInfo = new HashMap<String,Object>(2){{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
            put("message", "获取成功");
        }};
        if(singleLogin){
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(token, token);
        }
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setStatus(HttpStatus.OK.value());
        resultMessage.setResultParam(authInfo);
        return resultMessage;
    }

    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousAccess
    @PostMapping(value = "/login")
    public ResultMessage login(@Validated AuthUser authUser, HttpServletRequest request) throws Exception {
        // 密码解密
        String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword());
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(authUser.getUsername(), password);
        Authentication authentication = authenticationManager.authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        String token = tokenProvider.createToken(authentication);
        final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUserDto, token, request);
        // 返回 token 与 用户信息
        Map<String,Object> authInfo = new HashMap<String,Object>(2){{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUserDto);
            put("message", "获取成功");
        }};
        if(singleLogin){
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(authUser.getUsername(),token);
        }
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setStatus(HttpStatus.OK.value());
        resultMessage.setResultParam(authInfo);
        return resultMessage;
    }

    @Log("获取自己的用户信息")
    @ApiOperation("获取自己的用户信息")
    @GetMapping(value = "/getUserInfo")
    @PreAuthorize("@el.check('getUserInfo')")
    public ResultMessage getUserInfo(){
        JwtUserDto jwtUserDto = (JwtUserDto) detailsService.loadUserByUsername(UserUtil.getCurrentUsername());
        Dict dict = Dict.create().set("currentUser", jwtUserDto)
                .set("message", "获取成功");
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setStatus(HttpStatus.OK.value());
        resultMessage.setResultParam(dict);
        return resultMessage;
    }

    @Log("获取验证码")
    @AnonymousAccess
    @ApiOperation("获取验证码")
    @GetMapping(value = "/getCode")
    public ResultMessage getCode(){
        // 算术类型 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 几位数运算，默认是两位
        captcha.setLen(2);
        // 获取运算的结果
        String result = captcha.text();
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        // 保存
        redisUtils.set(uuid, result, expiration, TimeUnit.MINUTES);
        // 验证码信息
        Map<String,Object> imgResult = new HashMap<String,Object>(2){{
            put("img", captcha.toBase64());
            put("uuid", uuid);
            put("message", "获取成功");
        }};
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setStatus(HttpStatus.OK.value());
        resultMessage.setResultParam(imgResult);
        return resultMessage;
    }

    @Log("退出登录")
    @ApiOperation("退出登录")
    @AnonymousAccess
    @DeleteMapping(value = "/logout")
    public ResultMessage logout(HttpServletRequest request){
        onlineUserService.logout(tokenProvider.getToken(request));
        ResultMessage resultMessage = new ResultMessage();
        resultMessage.setStatus(HttpStatus.OK.value());
        return resultMessage;
    }
}
